package com.yoki.cloud.common.aop.interceptor.db;


import com.yoki.cloud.common.utils.aop.security.EncryptDecryptAnnoUtils;
import com.yoki.cloud.common.utils.aop.security.PrivacyDisplayAnnoUtils;
import com.yoki.cloud.common.utils.aop.security.PrivacyDisplayUtils;
import com.yoki.cloud.common.utils.aop.security.annotation.Decrypt;
import com.yoki.cloud.common.utils.bytes.ByteUtils;
import com.yoki.cloud.common.utils.encrypt.EncryptUtils;
import lombok.Getter;
import lombok.Setter;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Properties;

/**
 * 拦截器，用于拦截查询数据库返回的数据<BR/>
 * 拦截后用于将加密的数据解密，然后返回<BR/>
 * 对标有{@link Decrypt}注解的字段数据解密
 *
 * @author SKY
 */
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class ResultInterceptor implements Interceptor {

    @Setter
    @Getter
    private String secret = "interceptor.db.secret" ;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result = invocation.proceed();
        if (null == result) {
            return null;
        }
        //判断传入的数据集是不是ArrayList类型
        if (result instanceof ArrayList) {
            ArrayList resultList = (ArrayList) result;
            //判断数据集是否为空
            if (null == resultList || resultList.size() == 0) {
                return result;
            }
            //数据不为空，获取数据集Iterator
            Iterator iter = resultList.iterator();
            //迭代获取数据
            while (iter.hasNext()) {
                //获取单个数据
                Object bean = iter.next();
                //获取类
                Class<?> clazz = bean.getClass();
                //获取字段数组
                Field[] fields = clazz.getDeclaredFields();
                if (null != fields && fields.length != 0) {
                    int length = fields.length;
                    for (int i = 0; i < length; i++) {
                        Field field = fields[i];
                        //判断数据是否需要解密
                        if (EncryptDecryptAnnoUtils.isNeedDecryptClass(bean)) {
                            decrypt(bean, field);
                        }
                        //判断数据是否需要隐私显示
                        if (PrivacyDisplayAnnoUtils.isNeedPrivacyDisplayClass(bean)) {
                            PrivacyDisplayUtils.privacyDisplay(bean, field);
                        }
                    }
                }

            }

            //判断数据是否需要隐私显示

        } else if (EncryptDecryptAnnoUtils.isNeedDecryptClass(result)) {
            decrypt(result);
        }
        return result;
    }

    /**
     * 将数据bean中的有解密注解的字段进行解密
     *
     * @param bean 数据bean
     * @throws IllegalAccessException
     */
    private void decrypt(Object bean) throws IllegalAccessException {
        //获取类
        Class<?> clazz = bean.getClass();
        //获取字段
        Field[] fields = clazz.getDeclaredFields();
        if (null != fields && fields.length != 0) {
            int length = fields.length;
            for (int i = 0; i < length; i++) {
                Field field = fields[i];
                decrypt(bean, field);
            }

        }
    }

    private void decrypt(Object bean, Field field) throws IllegalAccessException {
        //判断字段是否有注解
        if (!EncryptDecryptAnnoUtils.isNeedDecryptField(field)) {
            return;
        }
        //判断字段是否为String类型
        if (field.getType() == String.class) {
            //设置非public的可访问
            field.setAccessible(true);
            String encryptValue = (String) field.get(bean);
            if(null != encryptValue && !"".equals(encryptValue)) {
                String decryptValue = new String(EncryptUtils.decryptAES(ByteUtils.decodeHex(encryptValue.toCharArray()), secret));
                field.set(bean, decryptValue);
            }
        }
    }


    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}
